照例先放上圖片方便對照。
昨天我們把圖片上半部的初始場景建立好了,包含 地圖、怪物、掉落物、玩家。然而這些物件之間並不是靜態的,想要讓他們在地圖上互動,我們就要先定義好相關互動事件的模型
(接續昨天的實作內容) 在 src/models/event.ts 新增以下程式碼定義事件模型
import { Player } from "./item";
export type Event = MoveEvent | AttackEvent | PickEvent;
export class MoveEvent {
  _tag: "MoveEvent" = "MoveEvent";
  player: Player; // 誰在移動 ?
  direction: "left" | "right"; // 往哪裡移動 ?
  constructor(player: Player, direction: "left" | "right") {
    this.player = player;
    this.direction = direction;
  }
}
export class AttackEvent {
  _tag: "AttackEvent" = "AttackEvent";
  player: Player; // 誰施放技能 ?
  skill: string; // 技能名稱
  range: { dx: number; dy: number }; // 技能範圍,這邊就不做傷害計算了,一律秒殺
  constructor(
    player: Player,
    skill: string,
    range: { dx: number; dy: number }
  ) {
    this.player = player;
    this.range = range;
    this.skill = skill;
  }
}
export class PickEvent {
  _tag: "PickEvent" = "PickEvent";
  player: Player;  // 誰在撿取物品
  constructor(player: Player) {
    this.player = player;
  }
}
有事件模型後,我們就可以在  src/models/map.ts 建立一個事件分配器,把各個事件分別交給不同的事件處理器來處理
  on(event: Event) {
    const { _tag } = event;
    if (_tag === "MoveEvent") this.onMove(event);
    else if (_tag === "AttackEvent") this.onAttack(event);
    else if (_tag === "PickEvent") this.onPick(event);
  }
實作 onMove, onAttack, onPick 三種事件處理方式
  private find(player: Player) {
    return this.grid
      .map((row, y) =>
        row
          .map((cell, x) =>
            cell.some((item) => item.id === player.id)
              ? ([x, y] as const)
              : undefined
          )
          .find((cell) => cell !== undefined)
      )
      .find((row) => row !== undefined);
  }
  private onMove({ player, direction }: MoveEvent) {
    const position = this.find(player);
    if (!position) return;
    const [x, y] = position;
    this.grid[y][x] = this.grid[y][x].filter((item) => item.id !== player.id);
    if (direction === "left") {
      this.grid[y][x - 1].push(player);
    } else if (direction === "right") {
      this.grid[y][x + 1].push(player);
    }
  }
  private onAttack({ player, range, skill }: AttackEvent) {
    const position = this.find(player);
    if (!position) return;
    const [x, y] = position;
    const { dx, dy } = range;
    const xStart = Math.min(x, x + dx);
    const xEnd = Math.max(x, x + dx);
    const yStart = Math.min(y, y + dy);
    const yEnd = Math.max(y, y + dy);
    for (let i = xStart; i < xEnd; i++) {
      for (let j = yStart; j < yEnd; j++) {
        this.grid[j][i] = this.grid[j][i]
          .map((item) => {
            if (item instanceof Monster) {
              console.log(`${player.name}使用${skill}擊敗了${item.name}`);
              return item.drop();
            } else {
              return item;
            }
          })
          .filter((item) => item !== undefined);
      }
    }
  }
  private onPick({ player }: PickEvent) {
    const position = this.find(player);
    if (!position) return;
    const [x, y] = position;
    this.grid[y][x] = this.grid[y][x]
      .map((item) => {
        if (item instanceof Dropped) {
          console.log(`${player.name}撿走了${item.name}`);
          return undefined;
        } else {
          return item;
        }
      })
      .filter((item) => item !== undefined);
  }
最後 index.ts 看起來會像是這樣
const map = new Map("森林小徑1", { colums: 50, rows: 1 });
const 煞氣a刀賊 = new Player("煞氣a刀賊");
const 乂正義黑騎乂 = new Player("乂正義黑騎乂");
const 紅寶 = new Monster("紅寶");
const 藍寶 = new Monster("藍寶");
const 刀賊向左走 = new MoveEvent(煞氣a刀賊, "left");
const 黑騎向右走 = new MoveEvent(乂正義黑騎乂, "right");
const 刀賊迴旋斬 = new AttackEvent(煞氣a刀賊, "迴旋斬", { dx: -1, dy: 1 });
const 刀賊撿取 = new PickEvent(煞氣a刀賊);
map.add(煞氣a刀賊, { x: 49, y: 0 });
map.add(乂正義黑騎乂, { x: 0, y: 0 });
map.add(紅寶, { x: 25, y: 0 });
map.add(藍寶, { x: 37, y: 0 });
for (let i = 0; i < 11; i++) {
  map.on(刀賊向左走);
  map.on(黑騎向右走);
}
map.on(刀賊迴旋斬);
map.on(刀賊向左走);
map.on(刀賊撿取);
是不是感覺越來越有畫面感了呢 